#include <core/common/global.h>
#include <core/common/file.h>
#include <core/common/string.h>

#ifdef OS_PLATFORM_LINUX
	#include <pwd.h>
#else
	#include <shlobj.h>
#endif

//
// class common::FileAttribute
//
core::common::FileAttribute::FileAttribute()
{
	directory_  = false;
	createTime_ = 0;
	modifyTime_ = 0;
	accessTime_ = 0;
	size_       = File::kInvalidSize;
}

//
// class common::File
//

#ifdef OS_PLATFORM_WINDOWS
	const char *core::common::File::kPathSeparator = "\\";
	const char  core::common::File::kPathCharacter = '\\';
#else
	const char *core::common::File::kPathSeparator = "/";
	const char  core::common::File::kPathCharacter = '/';
#endif

core::common::File::File()
{
	handle_   = NULL;
	openMode_ = File::kModeNone;
}

core::common::File::~File()
{
	if( isOpened() )
	{
		close();
	}
}

bool core::common::File::makeDirectory( const std::string &path, int mode, bool recursion /* = false */ )
{
	std::string normalPath = File::normalizePathTo(path);

	if( !recursion )
	{
#ifdef OS_PLATFORM_WINDOWS
		return ::CreateDirectoryA(normalPath.c_str(), NULL) == TRUE;
#else
		return mkdir(normalPath.c_str(), (0 == mode) ? 0755 : mode) == 0;
#endif
	}

	size_t lastPos = 0;
	size_t findPos = 0;
	while( lastPos < normalPath.size() )
	{
		findPos = normalPath.find(core::common::File::kPathCharacter, lastPos);
		if( findPos == lastPos )
		{
			lastPos = findPos + 1;
			continue;
		}

		std::string subPath = normalPath.substr(0, (std::string::npos != findPos) ? findPos : normalPath.size());
		lastPos = (std::string::npos != findPos) ? findPos + 1 : normalPath.size();
		if( File::isExists(subPath) )
		{
			continue;
		}
		if( !File::makeDirectory(subPath, mode, false) )
		{
			return false;
		}
	}

	return true;
}

bool core::common::File::isAbsolutePath( const std::string &path )
{
	std::string normalPath = File::normalizePathTo(path);

#ifdef OS_PLATFORM_WINDOWS
	return normalPath.size() > 0 && ('\\' == normalPath[0] || normalPath.find(':') != std::string::npos);
#else
	return normalPath.size() > 0 && '/' == normalPath[0];
#endif
}

std::string core::common::File::getModulePath( void *param /* = NULL */ )
{
	std::string path;
	char buffer[File::kMaxPathLength * 4] = {0};

#ifdef OS_PLATFORM_WINDOWS
	DWORD pathLength = ::GetModuleFileNameA((HMODULE)param, buffer, File::kMaxPathLength * 4 - 1);
	if( pathLength >= 0 )
	{
		buffer[pathLength] = '\0';
		path = buffer;
	}
#else
	ssize_t pathLength = readlink("/proc/self/exe", buffer, File::kMaxPathLength * 4 - 1);
	if( pathLength >= 0 )
	{
		buffer[pathLength] = '\0';
		path = buffer;
	}
#endif

	return path;
}

std::string core::common::File::getModuleDirectory( void *param /* = NULL */ )
{
	std::string path = File::getModulePath(param);
	return getDirectoryFromPath(path);
}

std::string core::common::File::getTempDirectory()
{
	std::string path;

#ifdef OS_PLATFORM_WINDOWS
	const size_t maxPathSize = 10240;
	path.resize(maxPathSize, 0);
	DWORD result = GetTempPathA((DWORD)maxPathSize - 1, (char *)path.c_str());
	if( result > 0 )
	{
		path.resize(result);
	}
	else
	{
		path.clear();
	}
#else
#ifdef ANDROID
	path = std::string("/sdcard/mobileiq/tmp");
#else
	path = std::string("/tmp");
#endif
#endif
	
	while( path.size() > 0 && File::kPathCharacter == path[path.size() - 1] )
	{
		path.erase(path.size() - 1, 1);
	}

	return path;
}

std::string core::common::File::getNullDevicePath()
{
#ifdef OS_PLATFORM_WINDOWS
	return std::string("NUL");
#else
	return std::string("/dev/null");
#endif
}

std::string core::common::File::getUserHomePath()
{
	std::string path;

#ifdef OS_PLATFORM_WINDOWS
	char buffer[MAX_PATH] = {0};
	if( SHGetFolderPathA(NULL, CSIDL_PROFILE, NULL, 0, buffer) == S_OK )
	{
		path = buffer;
	}
#else
	struct passwd *pwd = getpwuid(getuid());
	if( NULL != pwd )
	{
		path = pwd->pw_dir;
	}
#endif

	while( path.size() > 0 && File::kPathCharacter == path[path.size() - 1] )
	{
		path.erase(path.size() - 1, 1);
	}
	return path;
}

std::string core::common::File::getModuleAbsPath( const std::string &name )
{
	core::common::StringArray paths;
	paths.push_back(getModuleDirectory());
	core::common::getEnvironmentVariable("PATH", paths);

	for ( core::common::StringArray::iterator iter = paths.begin(); iter != paths.end(); iter ++ )
	{
		std::string tryPath = core::common::String::format("%s%s%s", iter->c_str(), kPathSeparator, name.c_str());
		if ( isExists(tryPath) )
		{
			return tryPath;
		}

		if ( std::string::npos != name.find(".exe") ) continue;

		tryPath = core::common::String::format("%s%s%s.exe", iter->c_str(), kPathSeparator, name.c_str());
		if ( isExists(tryPath) )
		{
			return tryPath;
		}
	}

	return "";
}

int core::common::File::normalizeName( std::string &name, const std::string &replaceString /* = "_" */ )
{
	int result = 0;
	
	result += core::common::String::replace(name, "/",  replaceString);
	result += core::common::String::replace(name, "\\", replaceString);
	result += core::common::String::replace(name, ":",  replaceString);
	result += core::common::String::replace(name, ";",  replaceString);
	result += core::common::String::replace(name, "*",  replaceString);
	result += core::common::String::replace(name, "?",  replaceString);
	result += core::common::String::replace(name, "\"", replaceString);
	result += core::common::String::replace(name, "<",  replaceString);
	result += core::common::String::replace(name, ">",  replaceString);
	result += core::common::String::replace(name, "|",  replaceString);
	result += core::common::String::replace(name, "&",  replaceString);

	return result;
}

int core::common::File::normalizePath( std::string &path )
{
#ifdef OS_PLATFORM_WINDOWS
	return core::common::String::replace(path, "/", File::kPathSeparator);
#else
	return core::common::String::replace(path, "\\", File::kPathSeparator);
#endif
}

std::string core::common::File::normalizeNameTo( const std::string &name, const std::string &replaceString /* = "_" */ )
{
	std::string strNormalName = name;
	File::normalizeName(strNormalName, replaceString);
	return strNormalName;
}

std::string core::common::File::normalizePathTo( const std::string &path )
{
	std::string normalPath = path;
	File::normalizePath(normalPath);
	return normalPath;
}

std::string core::common::File::getNameFromPath( const std::string &path )
{
	std::string normalPath = path;
	File::normalizePath(normalPath);

	size_t pos = normalPath.rfind(File::kPathCharacter);
	if( std::string::npos == pos )
	{
		return normalPath;
	}
	else if( pos + 1 >= normalPath.size() )
	{
		return std::string("");
	}
	else
	{
		return normalPath.substr(pos + 1, normalPath.size() - pos - 1);
	}
}

std::string core::common::File::getDirectoryFromPath( const std::string &path )
{
	std::string normalPath = File::normalizePathTo(path);
	size_t pos = normalPath.rfind(File::kPathCharacter);
	if( std::string::npos == pos )
	{
#ifdef OS_PLATFORM_WINDOWS
		pos = normalPath.rfind(':');
		if( std::string::npos == pos )
		{
			return std::string("");
		}
		else
		{
			pos ++;
		}
#else
		return std::string("");
#endif
	}
	
	if( 0 == pos && normalPath.size() > 0 ) pos ++;
	return normalPath.substr(0, pos);
}

std::string core::common::File::getExtensionName( const std::string &path )
{
	std::string normalPath = File::normalizePathTo(path);

	size_t nPos = normalPath.rfind('.');
	if( std::string::npos == nPos || (nPos + 1) == normalPath.size() )
	{
		return std::string("");
	}

	return normalPath.substr(nPos + 1);
}

bool core::common::File::moveFile( const std::string &source, const std::string &dest, bool overwrite )
{
	std::string normalSource = File::normalizePathTo(source);
	std::string normalDest   = File::normalizePathTo(dest);

	if( overwrite && core::common::File::isExists(dest) )
	{
		core::common::File::deleteFile(dest);
	}

#ifdef OS_PLATFORM_WINDOWS
	return ::MoveFileA(normalSource.c_str(), normalDest.c_str()) == TRUE;
#else
	return rename(normalSource.c_str(), normalDest.c_str()) == 0;
#endif
}

bool core::common::File::copyFile( const std::string &source, const std::string &dest, bool overwrite )
{
	std::string normalSource = File::normalizePathTo(source);
	std::string normalDest   = File::normalizePathTo(dest);

#ifdef OS_PLATFORM_WINDOWS
	return ::CopyFileA(normalSource.c_str(), normalDest.c_str(), !overwrite) == TRUE;
#else
	if( normalSource == normalDest )
	{
		core::common::setErrorCode(ERROR_ALREADY_EXISTS);
		return false;
	}
	else if( !overwrite && core::common::File::isExists(normalDest) )
	{
		core::common::setErrorCode(ERROR_ALREADY_EXISTS);
		return false;
	}

	core::common::File sourceFile, destFile;
	uint32 errorCode = ERROR_SUCCESS;
	bool success = false;
	char *dataBuffer = NULL;
	const size_t bufferSize = 102400;

	do 
	{
		dataBuffer = (char *)malloc(bufferSize);
		if( NULL == dataBuffer )
		{
			errorCode = core::common::getErrorCode();
			break;
		}

		if( !sourceFile.open(normalSource, core::common::File::kModeRead|core::common::File::kModeBinary) ||
			!destFile.open(normalDest, core::common::File::kModeWrite|core::common::File::kModeBinary) )
		{
			errorCode = core::common::getErrorCode();
			break;
		}

		while( true )
		{
			size_t nSizeRead = sourceFile.read(dataBuffer, 1, bufferSize);
			if( nSizeRead == 0 )
			{
				success = true;
				break;
			}

			if( destFile.write(dataBuffer, 1, nSizeRead) != nSizeRead )
			{
				errorCode = core::common::getErrorCode();
				break;
			}
		}
	} while (false);

	if( NULL != dataBuffer ) free(dataBuffer);
	if( sourceFile.isOpened() ) sourceFile.close();
	if( destFile.isOpened() ) destFile.close();

	if( ERROR_SUCCESS != errorCode )
	{
		core::common::setErrorCode(errorCode);
	}

	return success;
#endif
}

bool   core::common::File::copyDirectory( const std::string &source, const std::string &dest, bool overwrite )
{
	if ( core::common::File::isExists(dest) )
	{
		if ( !overwrite)
		{
			return false;
		}
	}
	else
	{
		if ( !core::common::File::makeDirectory(dest, 0) && overwrite)
		{
			return false;
		}
	}

	core::common::FileAttributes files;
	if( !core::common::File::getFileList( source, files ) )
	{
		return false;
	}
	for( core::common::FileAttributes::const_iterator itrItem = files.begin(); itrItem != files.end(); ++itrItem )
	{
		const std::string &name = itrItem->first;
		const core::common::FileAttribute &item = itrItem->second;
		const std::string sourceFile = core::common::String::format("%s/%s", source.c_str(), name.c_str());
		const std::string destFile = core::common::String::format("%s/%s", dest.c_str(), name.c_str());

		if( item.directory_ )
		{
			if ( !copyDirectory( sourceFile, destFile, overwrite) )
			{
				return false;
			}
			continue;
		}
		if( !core::common::File::copyFile( sourceFile, destFile, overwrite ) )
		{
			return false;
		}
	}

	return true;
}

bool core::common::File::deleteFile( const std::string &file )
{
	std::string normalFile = File::normalizePathTo(file);

#ifdef OS_PLATFORM_WINDOWS
	return ::DeleteFileA(normalFile.c_str()) == TRUE;
#else
	return unlink(normalFile.c_str()) == 0;
#endif
}

bool core::common::File::deleteDirectory( const std::string &path, bool force /* = false */ )
{
	std::string normalPath = File::normalizePathTo(path);

	if( force )
	{
		core::common::FileAttributes files;
		if( !core::common::File::getFileList(normalPath, files ) )
		{
			return false;
		}

		for( core::common::FileAttributes::iterator itr = files.begin(); itr != files.end(); itr ++ )
		{
			std::string filePath = core::common::String::format("%s/%s", normalPath.c_str(), itr->first.c_str());
			if( itr->second.directory_ )
			{
				if( !core::common::File::deleteDirectory(filePath, force) )
				{
					return false;
				}
				continue;
			}
			if( !core::common::File::deleteFile(filePath) )
			{
				return false;
			}
		}
	}

#ifdef OS_PLATFORM_WINDOWS
	return ::RemoveDirectoryA(normalPath.c_str()) == TRUE;
#else
	return rmdir(normalPath.c_str()) == 0;
#endif
}

bool core::common::File::isExists( const std::string &file )
{
	std::string normalFile = File::normalizePathTo(file);

#ifdef OS_PLATFORM_WINDOWS
	return ::PathFileExistsA(normalFile.c_str()) == TRUE;
#else
	struct stat fileStat;
	return ::stat(normalFile.c_str(), &fileStat) == 0 || errno != ENOENT;
#endif
}

bool core::common::File::isDirectoryEmpty( const std::string &path )
{
	core::common::FileAttributes files;
	if( !getFileList(path, files, false, 1) )
	{
		return true;
	}

	return files.size() == 0;
}

uint64 core::common::File::getSize( const std::string &file0 )
{
	uint64 fileSize = core::common::File::kInvalidSize;
	std::string strNormalFile = File::normalizePathTo(file0);

#ifdef OS_PLATFORM_WINDOWS
	WIN32_FILE_ATTRIBUTE_DATA fileAttr;
	if( GetFileAttributesExA(strNormalFile.c_str(), GetFileExInfoStandard, &fileAttr) )
	{
		fileSize = (((uint64)(fileAttr.nFileSizeHigh)) << 32) + (uint64)(fileAttr.nFileSizeLow);
	}
#else
	struct stat fileStat;
	if( ::stat(strNormalFile.c_str(), &fileStat) == 0 )
	{
		fileSize = (uint64)fileStat.st_size;
	}
#endif

	return fileSize;
}

uint64 core::common::File::getSize( const int file )
{
	uint64 fileSize = core::common::File::kInvalidSize;

#ifdef OS_PLATFORM_WINDOWS
	LARGE_INTEGER attrSize;
	if( ::GetFileSizeEx((HANDLE)_get_osfhandle(file), &attrSize) )
	{
		fileSize = (uint64)attrSize.QuadPart;
	}
#else
	struct stat fileStat;
	if( ::fstat(file, &fileStat) == 0 )
	{
		fileSize = (uint64)fileStat.st_size;
	}
#endif

	return fileSize;
}

bool core::common::File::setSize( const std::string &file0, uint64 newSize )
{
	std::string normalFile = File::normalizePathTo(file0);

#ifdef OS_PLATFORM_WINDOWS
	bool success = false;

	HANDLE fileHandle = ::CreateFileA(normalFile.c_str(), 
		GENERIC_READ|GENERIC_WRITE, 
		FILE_SHARE_READ|FILE_SHARE_WRITE, 
		NULL,
		OPEN_ALWAYS,
		FILE_ATTRIBUTE_NORMAL,
		NULL);
	if( INVALID_HANDLE_VALUE == fileHandle )
	{
		return false;
	}

	LARGE_INTEGER attrSize, newPos;
	attrSize.QuadPart = newSize;
	if( ::SetFilePointerEx(fileHandle, attrSize, &newPos, FILE_BEGIN) && ::SetEndOfFile(fileHandle) )
	{
		success = true;
	}

	CloseHandle(fileHandle);
	fileHandle = INVALID_HANDLE_VALUE;

	return success;
#else
	return ::truncate(normalFile.c_str(), newSize) == 0;
#endif
}


bool core::common::File::setSize( const int file, uint64 newSize )
{
#ifdef OS_PLATFORM_WINDOWS
	bool success = false;

	HANDLE fileHandle = (HANDLE)_get_osfhandle(file);
	if( INVALID_HANDLE_VALUE == fileHandle )
	{
		return false;
	}

	LARGE_INTEGER attrSize, zeroSize, oldPos, newPos;

	zeroSize.QuadPart = 0;
	attrSize.QuadPart = newSize;
	if( ::SetFilePointerEx(fileHandle, zeroSize, &oldPos, FILE_CURRENT) && // backup current position
		::SetFilePointerEx(fileHandle, attrSize, &newPos, FILE_BEGIN) && 
		::SetEndOfFile(fileHandle) &&
		// restore old position
		::SetFilePointerEx(fileHandle, oldPos, &newPos, FILE_BEGIN) )
	{
		success = true;
	}

	return success;
#else
	return ::ftruncate(file, newSize) == 0;
#endif
}

#ifdef OS_PLATFORM_WINDOWS
time_t __WinFileTime2UnixTime( const FILETIME &timeValue )
{
	LONGLONG timeTick = (((LONGLONG)timeValue.dwHighDateTime) << 32) + (LONGLONG)(timeValue.dwLowDateTime);
	return (time_t)((timeTick - 116444736000000000L) / 10000000);  
}
#endif

bool core::common::File::getAttribue( const std::string &file0, core::common::FileAttribute &attribute )
{
	std::string normalFile = File::normalizePathTo(file0);

#ifdef OS_PLATFORM_WINDOWS
	WIN32_FILE_ATTRIBUTE_DATA fileAttr;
	if( GetFileAttributesExA(normalFile.c_str(), GetFileExInfoStandard, &fileAttr) )
	{
		attribute.directory_  = (fileAttr.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
		attribute.createTime_ = __WinFileTime2UnixTime(fileAttr.ftCreationTime);
		attribute.modifyTime_ = __WinFileTime2UnixTime(fileAttr.ftLastWriteTime);
		attribute.accessTime_ = __WinFileTime2UnixTime(fileAttr.ftLastAccessTime);
		attribute.size_       = (((uint64)(fileAttr.nFileSizeHigh)) << 32) + (uint64)(fileAttr.nFileSizeLow);
		return true;
	}
#else
	struct stat fileStat;
	if( ::stat(normalFile.c_str(), &fileStat) == 0 )
	{
		attribute.directory_  = (fileStat.st_mode & S_IFDIR) != 0;
		attribute.createTime_ = fileStat.st_ctime;
		attribute.modifyTime_ = fileStat.st_mtime;
		attribute.accessTime_ = fileStat.st_atime;
		attribute.size_       = fileStat.st_size;
		return true;
	}
#endif
	else
	{
		return false;
	}
}

core::common::FileAttribute core::common::File::getAttribue( const std::string &file0 )
{
	core::common::FileAttribute attribute;
	File::getAttribue(file0, attribute);
	return attribute;
}

bool core::common::File::getFileList( const std::string &directory0, core::common::FileAttributes &files, bool fillAttr, size_t maxCount )
{
	std::string normalPath = File::normalizePathTo(directory0);

#ifdef OS_PLATFORM_WINDOWS
	WIN32_FIND_DATAA findData;
	HANDLE findHandle = INVALID_HANDLE_VALUE;
	std::string findName = core::common::String::format("%s\\*", normalPath.c_str());
	findHandle = FindFirstFileA(findName.c_str(), &findData);
	if( INVALID_HANDLE_VALUE == findHandle )
	{
		return false;
	}

	// clean old files
	files.clear();

	do 
	{
		if( strcmp(findData.cFileName, ".") == 0 ||
			strcmp(findData.cFileName, "..") == 0 )
		{
			// self or parent
			continue;
		}

		std::string name(findData.cFileName);
		core::common::FileAttribute &attribute = files[name];

		attribute.directory_  = (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
		attribute.createTime_ = __WinFileTime2UnixTime(findData.ftCreationTime);
		attribute.modifyTime_ = __WinFileTime2UnixTime(findData.ftLastWriteTime);
		attribute.accessTime_ = __WinFileTime2UnixTime(findData.ftLastAccessTime);
		attribute.size_       = (((uint64)(findData.nFileSizeHigh)) << 32) + (uint64)(findData.nFileSizeLow);

		if( maxCount > 0 && files.size() >= maxCount )
		{
			break;
		}
	} while (FindNextFileA(findHandle, &findData));

	FindClose(findHandle);
	findHandle = INVALID_HANDLE_VALUE;
#else
	DIR *dirHandle = opendir(normalPath.c_str());
	if( NULL == dirHandle )
	{
		return false;
	}

	// clean old files
	files.clear();

	struct dirent *item = NULL;
	while( (item = readdir(dirHandle)) != NULL )
	{
		if( strcmp(item->d_name, ".") == 0 ||
			strcmp(item->d_name, "..") == 0 )
		{
			// self or parent
			continue;
		}

		std::string name(item->d_name);
		core::common::FileAttribute &attribute = files[name];

		if( fillAttr )
		{
			std::string filePath = core::common::String::format("%s/%s", normalPath.c_str(), name.c_str());
			File::getAttribue(filePath, attribute);
		}
		if( maxCount > 0 && files.size() >= maxCount )
		{
			break;
		}
	}

	closedir(dirHandle);
	dirHandle = NULL;
#endif

	return true;
}

FILE * core::common::File::getHandle()
{
	return handle_;
}

bool core::common::File::open( const std::string &file, int mode )
{
	VALIDATE_ERROR_RETURN(isOpened(), ERROR_ALREADY_EXISTS, false);

	file_ = File::normalizePathTo(file);

	// read or write
	char modeString[64] = {0};
	if( mode & kModeRead )	strcpy(modeString, (mode & kModeWrite) ? "r+" : "r");
	else if( mode & kModeWrite ) strcpy(modeString, "w");
	else strcpy(modeString, "r+");

	// text or binary
	if( mode & kModeBinary ) strcat(modeString, "b");
	else if( mode & kModeText ) strcat(modeString, "t");
	else strcat(modeString, "b"); // default as binary

	handle_ = fopen(file_.c_str(), modeString);
	if( NULL == handle_ )
	{
		return false;
	}

	openMode_ = mode;
	if( mode & kModeLock && !lockRange(0, core::common::kLow32BitsMask, true) )
	{
		uint32 errorCode = core::common::getErrorCode();

		fclose(handle_);
		handle_ = NULL;

		core::common::setErrorCode(errorCode);
		return false;
	}

	return NULL != handle_;
}

bool core::common::File::close()
{
	VALIDATE_ERROR_RETURN(!isOpened(), ERROR_NOT_READY, false);

	if( openMode_ & kModeLock )
	{
		unlockRange(0, core::common::kLow32BitsMask);
	}

	fclose(handle_);
	handle_ = NULL;

	return true;
}

bool core::common::File::flush()
{
	VALIDATE_ERROR_RETURN(!isOpened(), ERROR_NOT_READY, false);

	return fflush(handle_) == 0;
}

size_t core::common::File::read( void *buffer, size_t chunkSize, size_t count )
{
	VALIDATE_ERROR_RETURN(!isOpened(), ERROR_NOT_READY, 0);

	return fread(buffer, chunkSize, count, handle_);
}

size_t core::common::File::readHuge( void *buffer, size_t size, size_t blockSize /* = 10240 */ )
{
	VALIDATE_ERROR_RETURN(!isOpened(), ERROR_NOT_READY, 0);

	size_t readTotalSize = 0;
	while( readTotalSize < size )
	{
		size_t wantSize = std::min<size_t>(size - readTotalSize, blockSize);
		size_t readSize = fread(((char *)buffer) + readTotalSize, 1, wantSize, handle_);
		readTotalSize += readSize;
		if( 0 == readSize )
		{
			break;
		}
	}

	return readTotalSize;
}

bool core::common::File::readLine( std::string &line, size_t maxCount )
{
	VALIDATE_ERROR_RETURN(!isOpened(), ERROR_NOT_READY, 0);

	line.resize(maxCount + 1, 0);

	char *buffer = (char *)line.c_str();
	buffer[maxCount] = 0;
	if( fgets(buffer, maxCount, handle_) == NULL )
	{
		line.clear();
		return false;
	}

	size_t newLength = strlen(buffer);
	line.resize(newLength);
	return true;
}

bool core::common::File::readLine( char *buffer, size_t maxCount )
{
	VALIDATE_ERROR_RETURN(!isOpened(), ERROR_NOT_READY, 0);

	if( fgets(buffer, maxCount, handle_) == NULL )
	{
		return false;
	}

	return true;
}

size_t core::common::File::readData( std::string &data, size_t maxCount )
{
	VALIDATE_ERROR_RETURN(!isOpened(), ERROR_NOT_READY, 0);

	const size_t bufferSize = 1024;
	size_t readBytes = 0;
	char tmpBuffer[bufferSize];

	while( readBytes < maxCount )
	{
		size_t count = std::min<size_t>(maxCount - readBytes, bufferSize);
		size_t returnBytes = fread(tmpBuffer, 1, count, handle_);

		if( returnBytes <= 0 )
		{
			break;
		}
		readBytes += returnBytes;
		data.append(tmpBuffer, returnBytes);
	}

	return readBytes;
}

size_t core::common::File::write( const void *buffer, size_t chunkSize, size_t count )
{
	VALIDATE_ERROR_RETURN(!isOpened(), ERROR_NOT_READY, 0);

	return fwrite(buffer, chunkSize, count, handle_);
}

size_t core::common::File::writeHuge( const void *buffer, size_t size, size_t blockSize )
{
	VALIDATE_ERROR_RETURN(!isOpened(), ERROR_NOT_READY, false);

	size_t savedSize = 0;
	while( savedSize < size )
	{
		size_t wantSize = std::min<size_t>(size - savedSize, blockSize);
		size_t writeSize = fwrite(((const char *)buffer) + savedSize, 1, wantSize, handle_);

		savedSize += writeSize;
		if( 0 == writeSize )
		{
			break;
		}
	}

	return savedSize;
}

size_t core::common::File::print( const char *format, ... )
{
	VALIDATE_ERROR_RETURN(!isOpened(), ERROR_NOT_READY, 0);

	va_list argList;
	va_start(argList, format);
	size_t length = vfprintf(handle_, format, argList);
	va_end(argList);

	return length;
}

bool core::common::File::seek( uint64 offset, int from )
{
	VALIDATE_ERROR_RETURN(!isOpened(), ERROR_NOT_READY, false);

	int mode = SEEK_SET;
	switch( from )
	{
	case kSeekBegin:
		mode = SEEK_SET;
		break;
	case kSeekCurrent:
		mode = SEEK_CUR;
		break;
	case kSeekEnd:
		mode = SEEK_END;
		break;
	default:
		break;
	}

	return fseek64(handle_, offset, mode) == 0;
}

bool core::common::File::setSize( uint64 newSize )
{
	VALIDATE_ERROR_RETURN(!isOpened(), ERROR_NOT_READY, false);

	return setSize(fileno(handle_), newSize);
}

uint64 core::common::File::getSize()
{
	VALIDATE_ERROR_RETURN(!isOpened(), ERROR_NOT_READY, File::kInvalidSize);

	return getSize(fileno(handle_));
}

uint64 core::common::File::getPosition()
{
	VALIDATE_ERROR_RETURN(!isOpened(), ERROR_NOT_READY, File::kInvalidSize);

	return ftell64(handle_);
}

bool core::common::File::lockRange( uint64 offset, uint64 size, bool toTry /* = false */ )
{
	VALIDATE_ERROR_RETURN(!isOpened(), ERROR_NOT_READY, false);

	bool result = false;
	int fileNo = fileno(handle_);

#ifdef OS_PLATFORM_WINDOWS
	OVERLAPPED overlapped;
	DWORD sizeLow  = (DWORD)(size & core::common::kLow32BitsMask);
	DWORD sizeHigh = (DWORD)((size >> 32) & core::common::kLow32BitsMask);
	DWORD flags = toTry ? (LOCKFILE_EXCLUSIVE_LOCK|LOCKFILE_FAIL_IMMEDIATELY) : LOCKFILE_EXCLUSIVE_LOCK;

	memset(&overlapped, 0, sizeof(overlapped));
	overlapped.Offset     = (DWORD)(offset & core::common::kLow32BitsMask);
	overlapped.OffsetHigh = (DWORD)((offset >> 32) & core::common::kLow32BitsMask);

	result = (::LockFileEx((HANDLE)_get_osfhandle(fileNo), flags, 0, sizeLow, sizeHigh, &overlapped) == TRUE);
#else

#ifndef ANDROID
	uint64 oldPosition = getPosition();
	if( oldPosition != offset && !seek(offset, File::kSeekBegin) )
	{
		return false;
	}

	int flags = toTry ? F_TLOCK : F_LOCK;
	off_t unlockSize = (core::common::kLow32BitsMask == size) ? 0 : (off_t)size;
    result = (lockf(fileNo, flags, unlockSize) == 0);

	if( oldPosition != offset && !seek(oldPosition, File::kSeekBegin) )
	{
		uint32 errorCode = core::common::getErrorCode();
		
		// unlock
		result = false;
		lockf(fileNo, F_ULOCK, unlockSize);

		core::common::setErrorCode(errorCode);
	}
#endif

#endif

	return result;
}

bool core::common::File::unlockRange( uint64 offset, uint64 size )
{
	VALIDATE_ERROR_RETURN(!isOpened(), ERROR_NOT_READY, false);

	bool result = false;
	int fileNo = fileno(handle_);

#ifdef OS_PLATFORM_WINDOWS
	OVERLAPPED overlapped;
	DWORD sizeLow  = (DWORD)(size & core::common::kLow32BitsMask);
	DWORD sizeHigh = (DWORD)((size >> 32) & core::common::kLow32BitsMask);
	
	memset(&overlapped, 0, sizeof(overlapped));
	overlapped.Offset     = (DWORD)(offset & core::common::kLow32BitsMask);
	overlapped.OffsetHigh = (DWORD)((offset >> 32) & core::common::kLow32BitsMask);

	result = (::UnlockFileEx((HANDLE)_get_osfhandle(fileNo), 0, sizeLow, sizeHigh, &overlapped) == TRUE);
#else

#ifndef ANDROID
	uint64 oldPosition = getPosition();

	if( oldPosition != offset && !seek(offset, File::kSeekBegin) )
	{
		return false;
	}

	off_t unlockSize = (core::common::kLow32BitsMask == size) ? 0 : (off_t)size;
	result = (lockf(fileNo, F_ULOCK, unlockSize) == 0);
	
	if( oldPosition != offset && !seek(oldPosition, File::kSeekBegin) )
	{
		result = false;
	}
#endif

#endif

	return result;
}

